﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace JVMTI_CONN_CONNECT
{
    public interface ILifeEvent
    {
        double Timestamp { get; }
        JvmtiMethodEnum MethodName { get; }
        string Object { get; }
        bool EmpahsizingEvent();
        bool HighlightingEvent { get; set; }
        StackTrace StackTrace { get; }
        IEnumerable<string> GetRelatedEvents(IEnumerable<ILifeEvent> collection);
        string Subject { get; }
    }

    //make internal.
    class LifeEvent : ILifeEvent
    {
        internal JvmtiEvent jEvent;
        public LifeEvent(JvmtiEvent jvmtiEvent)
        {
            jEvent = jvmtiEvent;
        }

        public double Timestamp
        {
            get { return jEvent.Timestamp; }
        }

        public JvmtiMethodEnum MethodName
        {
            get { return jEvent.MethodName; }
        }
        public virtual string Object
        {
            get
            {
                var dict = jEvent.ExtendedArguments as IDictionary<string, object>;
                if (dict.ContainsKey("Object")) return dict["Object"].ToString();
                //this means it is never used.
                return null;
            }
        }
        public virtual StackTrace StackTrace
        {
            get
            {
                var dict = jEvent.ExtendedArguments as IDictionary<string, object>;
                if (dict.ContainsKey("StackTrace")) return dict["StackTrace"] as StackTrace;
                //this means it is never used.
                return null;
            }
        }

        public virtual bool EmpahsizingEvent()
        {
            //emphasizing is white. Waiting events should be emphasized.
            return (jEvent.MethodName & (JvmtiMethodEnum.ThreadSleep | JvmtiMethodEnum.MonitorContendedEnter | JvmtiMethodEnum.ObjectWait | JvmtiMethodEnum.ThreadJoin | JvmtiMethodEnum.SemaphoreAcquire)) != 0;
        }
        public override string ToString()
        {
            return jEvent.ToString();
        }

        public virtual IEnumerable<string> GetRelatedEvents(IEnumerable<ILifeEvent> events)
        {
            return null;
            // for thread life event, return locks holding. For resource life event, return threads contending at this time.
        }


        public virtual string Subject
        {
            get { return jEvent.SubjectThread; }
        }


        //when this satisifies a query.
        public bool HighlightingEvent { get; set; }
    }

    class ThreadLifeEvent : LifeEvent
    {
        public ThreadLifeEvent(JvmtiEvent jEvent) : base(jEvent) { }
        public override IEnumerable<string> GetRelatedEvents(IEnumerable<ILifeEvent> events)
        {
            //if (!events.Contains<ILifeEvent>(this)) throw new InvalidOperationException();
            var candidateEvents = events.AsParallel<ILifeEvent>().Where<ILifeEvent>(e => e.Subject == jEvent.SubjectThread && (e.MethodName & (JvmtiMethodEnum.MonitorContendedEnter)) != 0 && e.Timestamp < Timestamp);
            var resources = candidateEvents.AsParallel<ILifeEvent>().Select<ILifeEvent, string>(e => e.Object);
            var resourceUsedByOthersInFuture = events.AsParallel<ILifeEvent>().Where<ILifeEvent>(e => e.Subject != Subject && e.Timestamp >= Timestamp && e.MethodName == JvmtiMethodEnum.MonitorContendedEntered).Select<ILifeEvent, string>(e => e.Object);//select others
            var holdingResources = resources.Except<string>(resources.Where<string>(resource => resourceUsedByOthersInFuture.Contains(resource))).ToList();
            //semaphors and others that have release method
            var locksUsed = events.AsParallel<ILifeEvent>().Where<ILifeEvent>(evt => evt.Subject == jEvent.SubjectThread && (evt.MethodName & (JvmtiMethodEnum.SemaphoreAcquired | JvmtiMethodEnum.SemaphoreReleased)) != 0 && evt.Timestamp < Timestamp).GroupBy<ILifeEvent, string>(evt => evt.Object);

            foreach (var group in locksUsed)
            {
                var currEvents = group.OrderBy<ILifeEvent, double>(evt => evt.Timestamp);
                if ((currEvents.Last().MethodName & (JvmtiMethodEnum.SemaphoreAcquired)) != 0)
                {
                    holdingResources.Add(group.Key);
                }
            }
            return holdingResources;
        }
    }

    class LifeEvents : List<ILifeEvent>
    {
        public LifeEvents(IEnumerable<ILifeEvent> events)
        {
            this.AddRange(events);
        }
    }

    class ResourceLifeEvent : LifeEvent
    {
        public ResourceLifeEvent(JvmtiEvent jEvent) : base(jEvent) { }
        public override bool EmpahsizingEvent()
        {
            //white means the lock is being used and is emphasized
            return (jEvent.MethodName & (JvmtiMethodEnum.MonitorContendedEntered | JvmtiMethodEnum.ObjectWait | JvmtiMethodEnum.SemaphoreAcquired)) != 0;
        }

        public override string Object
        {
            get
            {
                //the object is the subject in the resource's view.
                return base.jEvent.SubjectThread;
            }
        }
        public override string Subject
        {
            get
            {
                return base.Object;
            }
        }

        public override IEnumerable<string> GetRelatedEvents(IEnumerable<ILifeEvent> events)
        {
            //if (!events.Contains<ILifeEvent>(this)) throw new InvalidOperationException();
            var threadEvents = events.AsParallel<ILifeEvent>().Where<ILifeEvent>(e => e.Timestamp <= Timestamp && (e.MethodName & (JvmtiMethodEnum.MonitorContendedEntered | JvmtiMethodEnum.MonitorContendedEnter | JvmtiMethodEnum.SemaphoreAcquired | JvmtiMethodEnum.ObjectWait | JvmtiMethodEnum.ObjectWaited | JvmtiMethodEnum.SemaphoreAcquire)) != 0).GroupBy<ILifeEvent, string>(e => e.Object);
            var contentionSet = new List<string>();
            foreach (var group in threadEvents)
            {
                var currEvents = group.OrderByDescending<ILifeEvent, double>(e => e.Timestamp);
                if ((currEvents.Last().MethodName & (JvmtiMethodEnum.MonitorContendedEnter | JvmtiMethodEnum.SemaphoreAcquired | JvmtiMethodEnum.ObjectWait)) != 0)
                {
                    contentionSet.Add(group.Key);
                }
            }
            return contentionSet;
        }
    }

    //temporarily make 2 separate sets of events.
}
